﻿using AutoMapper;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using System.Linq.Expressions;
using TencentCloud.Kms.V20190118.Models;
using ZenSystemService.Api.Data;
using ZenSystemService.Api.DTOs;
using ZenSystemService.Api.DTOs.Auth;
using ZenSystemService.Api.DTOs.Product;
using ZenSystemService.Api.DTOs.ProductType;
using ZenSystemService.Api.Entities;
using ZenSystemService.Api.Utils;

namespace ZenSystemService.Api.Services
{
    public class ProductService
    {
        private readonly ApplicationDbContext _context;
        private readonly IMapper _mapper;

        public ProductService(ApplicationDbContext context, IMapper mapper)
        {
            _context = context;
            _mapper = mapper;
        }        

        public async Task<IEnumerable<Product>> LabelProductsAsync(User admin, ClientLoginRequestDTO login)
        {
            var productTypes = login.ProductTypes;
            //先把长时间没有轮询的产品释放掉
            var productsToRelease = _context.Products.Where(p => p.LastVisitedDate == null || p.LastVisitedDate < DateTime.Now.AddMinutes(-2));
            foreach (var productToRelease in productsToRelease)
            {
                productToRelease.Status = "0";
            }
            await _context.SaveChangesAsync();

            Product? product = null;
            //如果客户端指定要某些序列号的产品，就把这些产品返回给它
            if (login.ProductUniqueNumbers != null && login.ProductUniqueNumbers.Any())
            {
                product = _context.Products.Include(p => p.ProductType)
                    .FirstOrDefault(p => p.Enabled == "1" && p.InstitutionId == admin.InstitutionId
                        && login.ProductUniqueNumbers.Any(pn => pn == p.UniqueNumber));
            }
            //没提供UniqueNumber或者根据UniqueNumber没有找到，都重新下发
            if (product == null)
            {
                //暂时只支持一个产品类型
                var productType = productTypes.First();
                product = _context.Products.Include(p => p.ProductType)
                    .FirstOrDefault(p => p.Enabled == "1" && p.Status == "0"
                        && p.InstitutionId == admin.InstitutionId
                        && p.ProductType != null
                        && p.ProductType.Name == productType);
            }
            if (product == null)
                throw new Exception($"没有能下发的 {productTypes[0]} 类型的产品");
            product.Status = "1";
            product.LastVisitedDate = DateTime.Now;
            _context.Products.Entry(product).State = EntityState.Modified;
            await _context.SaveChangesAsync();

            return new List<Product> { product };
        }

        public async Task<ProductQueryResultDTO> UpdateProductMacAsync(ProductUpdateMacRequestDTO req)
        {
            var productWithMac = await _context.Products.FirstOrDefaultAsync(p => p.MacAddress == req.MAC);
            if (productWithMac != null)
                throw new Exception("该MAC已绑定过");
            var products = _context.Products.Where(p => p.UniqueNumber == req.UniqueNumber);
            var product = products.FirstOrDefault();
            if(product != null)
            {
                product.MacAddress = req.MAC;
                await _context.SaveChangesAsync();
            }
            else
            {
                throw new Exception("找不到产品");
            }
            return _mapper.Map<ProductQueryResultDTO>(product);
        }

        /// <summary>
        /// 根据ID更新产品信息，目前只有更新Alias的需求
        /// </summary>
        /// <param name="req"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public async Task<ProductQueryResultDTO> UpdateProductByIdAsync(ProductUpdateRequestDTO req)
        {
            var product = _context.Products.FirstOrDefault(p => p.ProductId == req.ProductId);
            if(product == null)
            {
                throw new Exception("产品找不到");
            }
            product.UpdateFrom(req);
            await _context.SaveChangesAsync();
            return _mapper.Map<ProductQueryResultDTO>(product);
        }

        public async Task<IEnumerable<ProductQueryResultDTO>> UnbindProductAsync(ClientLogoutRequestDTO req)
        {
            var products = _context.Products.Where(p => p.UniqueNumber != null && req.ProductUniqueNumbers.Any(rn => rn == p.UniqueNumber));
            
            if (products != null && products.Any())
            {
                await products.ForEachAsync(p => { p.Status = "0"; });
                await _context.SaveChangesAsync();
            }
            else
            {
                throw new Exception("序列码不正确，找不到产品");
            }
            return _mapper.Map<IEnumerable<ProductQueryResultDTO>>(products);
        }

        public async Task<ReturnObjectT<IEnumerable<ProductQueryResultDTO>>> GetProductsAsync(ReceiveObject req)
        {
            string orderBy = req.Order;
            var splited = orderBy.Split(' ');
            var orderField = "";
            var orderMethod = "";
            if (splited.Length > 0)
            {
                orderField = $"{splited[0]}";
            }
            if (splited.Length > 1)
            {
                orderMethod = splited[1];
            }

            var products = _context.Products.Include(o => o.Institution)
                .Include(o => o.ProductType).ThenInclude(pt => pt.DeviceType)
                .Include(p => p.InstitutionSalesOrder)
                .AsQueryable();
            var predicates = req.Where.GeneratePredicates<Product>();
            foreach(var predicate in predicates)
            {
                products = products.Where(predicate);
            }
            var totalCount = products.Count();
            int index = (req.Page - 1 < 0 ? 0 : req.Page - 1) * req.PageSize;
            products = products.Skip(index).Take(req.PageSize);
            var productsDTO =_mapper.Map<IEnumerable<ProductQueryResultDTO>>(products);

            ReturnObjectT<IEnumerable<ProductQueryResultDTO>> _returnObject = new ReturnObjectT<IEnumerable<ProductQueryResultDTO>>();
            _returnObject.Code = 1;
            _returnObject.Data = productsDTO;
            _returnObject.RecordTotalCount = totalCount;
            return _returnObject;
        }
    }
}